| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- 'use client';
- import { useState, useEffect, useCallback } from 'react';
- import { fetchApi } from '@/lib/utils/client';
- import type { ActiveSessionResponse, SessionHistoryResponse, SessionHistoryItem } from '@/types/response/crew/session';
- import { Button } from '@/components/ui/button';
- import { Input } from '@/components/ui/input';
- type Props = { crewID: number };
- export default function CrewSessionTab({ crewID }: Props)
- {
- const [activeSession, setActiveSession] = useState<ActiveSessionResponse>(null);
- const [history, setHistory] = useState<SessionHistoryItem[]>([]);
- const [historyTotal, setHistoryTotal] = useState(0);
- const [loading, setLoading] = useState(true);
- const [sessionTitle, setSessionTitle] = useState('');
- const [starting, setStarting] = useState(false);
- const [ending, setEnding] = useState(false);
- const fetchActiveSession = useCallback(async () => {
- try {
- const res = await fetchApi<ActiveSessionResponse>(`/api/studio/crew/session/active/${crewID}`);
- setActiveSession(res.data ?? null);
- } catch {}
- }, [crewID]);
- const fetchHistory = useCallback(async () => {
- try {
- const res = await fetchApi<SessionHistoryResponse>(`/api/studio/crew/session/history/${crewID}?page=1&perPage=10`);
- setHistory(res.data?.list ?? []);
- setHistoryTotal(res.data?.total ?? 0);
- } catch {}
- }, [crewID]);
- useEffect(() => {
- setLoading(true);
- Promise.all([fetchActiveSession(), fetchHistory()]).finally(() => setLoading(false));
- }, [fetchActiveSession, fetchHistory]);
- const handleStart = async () => {
- if (!sessionTitle.trim()) { alert('방송 제목을 입력해 주세요.'); return; }
- setStarting(true);
- try {
- await fetchApi('/api/crew/session/start', { method: 'POST', body: { crewID, title: sessionTitle.trim() } });
- setSessionTitle('');
- fetchActiveSession();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : '세션 시작에 실패했습니다.');
- } finally { setStarting(false); }
- };
- const handleEnd = async () => {
- if (!activeSession) return;
- if (!confirm('크루 방송을 종료하시겠습니까?\n종료 시 크루원에게 정산 결과가 전송됩니다.')) return;
- setEnding(true);
- try {
- await fetchApi('/api/crew/session/end', { method: 'POST', body: { crewSessionID: activeSession.crewSessionID } });
- setActiveSession(null);
- fetchHistory();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : '세션 종료에 실패했습니다.');
- } finally { setEnding(false); }
- };
- if (loading) return <p className="studio-page__empty">준비 중...</p>;
- return (
- <>
- {activeSession ? (
- <div className="session-active">
- <div className="session-active__card">
- <div className="session-active__top">
- <div className="session-active__info">
- <span className="session-active__session-title">{activeSession.title}</span>
- <span className={`studio-page__badge studio-page__badge--${activeSession.status === 'Active' ? 'active' : 'warning'}`}>
- {activeSession.status === 'Inviting' ? '동의 대기 중' : '방송 중'}
- </span>
- </div>
- <Button variant="destructive" size="sm" onClick={handleEnd} disabled={ending || activeSession.status === 'Inviting'}>
- {ending ? '종료 중...' : '방송 종료'}
- </Button>
- </div>
- <div className="session-active__stats">
- <div className="session-active__stat"><div className="session-active__stat-value">{activeSession.totalAmount.toLocaleString()}원</div><div className="session-active__stat-label">총 후원액</div></div>
- <div className="session-active__stat"><div className="session-active__stat-value">{activeSession.totalDonationCount}건</div><div className="session-active__stat-label">후원 건수</div></div>
- <div className="session-active__stat"><div className="session-active__stat-value">{activeSession.consents.filter(c => c.isConsented).length}/{activeSession.consents.length}</div><div className="session-active__stat-label">동의 현황</div></div>
- </div>
- <div className="session-consents">
- <div className="session-consents__title">크루원 동의 현황</div>
- <div className="session-consents__list">
- {activeSession.consents.map(c => (
- <div key={c.crewMemberID} className={`session-consents__item session-consents__item--${c.isConsented ? 'consented' : 'pending'}`}>
- <span className="session-consents__icon">{c.isConsented ? '✓' : '⏳'}</span>{c.nickname}
- </div>
- ))}
- </div>
- </div>
- {activeSession.status === 'Active' && activeSession.summaries.length > 0 && (
- <div style={{ marginTop: 16 }}>
- <table className="studio-page__table">
- <thead><tr><th>순위</th><th>크루원</th><th>후원액</th><th>건수</th><th>기여율</th></tr></thead>
- <tbody>
- {activeSession.summaries.map(s => (
- <tr key={s.crewMemberID}><td>{s.rank}위</td><td>{s.nickname}</td><td>{s.totalAmount.toLocaleString()}원</td><td>{s.donationCount}건</td><td>{s.contributionRate.toFixed(1)}%</td></tr>
- ))}
- </tbody>
- </table>
- </div>
- )}
- </div>
- </div>
- ) : (
- <div className="session-start">
- <div className="session-start__title">새 크루 방송 시작</div>
- <div className="session-start__form">
- <Input className="session-start__input" placeholder="방송 제목을 입력하세요" value={sessionTitle} onChange={e => setSessionTitle(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleStart()} />
- <Button onClick={handleStart} disabled={starting}>{starting ? '시작 중...' : '방송 시작'}</Button>
- </div>
- </div>
- )}
- <div className="session-history">
- <div className="session-history__title">지난 방송 ({historyTotal}건)</div>
- <div className="studio-page__table-wrap">
- <table className="studio-page__table">
- <thead><tr><th>제목</th><th>총 후원액</th><th>건수</th><th>시작</th><th>종료</th></tr></thead>
- <tbody>
- {history.length === 0 ? (
- <tr><td colSpan={5} className="studio-page__empty">아직 진행한 크루 방송이 없습니다.</td></tr>
- ) : history.map(h => (
- <tr key={h.id}><td>{h.title}</td><td>{h.totalAmount.toLocaleString()}원</td><td>{h.totalDonationCount}건</td><td>{h.startedAt ? new Date(h.startedAt).toLocaleString('ko-KR') : '-'}</td><td>{h.endedAt ? new Date(h.endedAt).toLocaleString('ko-KR') : '-'}</td></tr>
- ))}
- </tbody>
- </table>
- </div>
- </div>
- </>
- );
- }
|